﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using VDataAdapter.BlockingQueue;
using VDataAdapter.QueueManagement;
using VDataAdapter.Communication;
using log4net;

namespace VDataAdapter.WorkerManagement
{
    public delegate void WorkerExceptionHandler(Worker sender, Exception ex);

    /// <summary>
    /// Manage all workers thread
    /// </summary>
    public class WorkerManager
    {
        protected readonly ILog _log = LogManager.GetLogger(typeof(WorkerManager));

        protected List<Worker> _workers = new List<Worker>(); // all my worker
        private IWorkerScheduler _workerScheduler;
        protected Object _workerLock = new Object();

        public WorkerExceptionHandler _workerExceptionHandler;

        private bool _isActive;

        //WorkerManager contructor
        public WorkerManager(IWorkerScheduler scheduler, WorkerExceptionHandler exceptionHandler)
        {
            
            _workerScheduler = scheduler;

            _workerExceptionHandler = new WorkerExceptionHandler(exceptionHandler);

        }

        //Schedule and return the list of workers for the manager to manage
        public void StartAllWorkers(IEventProviderQueue eventQueue, VDataCommunicator comm)
        {
            _log.Info("Starting all workers.");
            //Set active
            _isActive = true;

            lock (_workerLock)
            {
                //Schedule all workers
                var workers = _workerScheduler.scheduleWorkers(eventQueue, comm);

                //Start all workers
                foreach (Worker worker in workers)
                {
                    worker.AcceptManager(this);
                    worker.StartWork(null);
                }
            }
        }

        //Stop all worker
        /// <summary>
        /// Call all workers and set them to not hardworking mode
        /// not checking queue mode and emergency stop
        /// </summary>
        public void StopAllWorkers()
        {
            _log.Info("Stopping all workers.");
            lock (_workerLock)
            {
                //Stop all workers
                foreach (Worker worker in _workers)
                {
                    worker.StopWork();
                    worker.IsHardWorking = false;
                    worker.EmergencyStop();
                }
            }

            //Set inactive
            _isActive = false;

            _log.Info("All workers stopped.");
        }

        //
        public void WaitPatiently()
        {
            _log.Info("Waiting for all workers to stop.");
            lock (_workerLock)
            {
                foreach (Worker w in _workers)
                {
                    w.WaitToStop();
                }
            }

            //Set inactive
            _isActive = false;

            _log.Info("All workers stopped.");
        }

        /// <summary>
        /// Atempt to stop workers before they go in to working mode
        /// </summary>
        public void HaltAllWorker()
        {
            _log.Info("Trying to stop all worker before they do work.");
            lock (_workerLock)
            {
                foreach (Worker worker in _workers)
                {
                    worker.EmergencyStop();                    
                }
            }

            //Set inactive
            _isActive = false;

            _log.Info("All workers stopped.");
        }

        /// <summary>
        /// Report exception back to manager
        /// with 'data' to contain information if needed
        /// </summary>
        /// <param name="worker"></param>
        /// <param name="data"></param>
        public void ReportException(Worker worker, Exception ex)
        {
            if (_isActive && _workerExceptionHandler != null)
            {
                _log.Error("Worker: "+worker.GetType().FullName+" thrown exception:",ex);
                _workerExceptionHandler(worker, ex);
            }
        }

        public void Report(Worker worker)
        {
            lock (_workerLock)
            {
                _workers.Add(worker);
            }
        }

        public void ReportFinished(Worker worker)
        {
            lock (_workerLock)
            {
                // wait to make sure worker has stop
                worker.WaitToStop(); 

                _workers.Remove(worker);
            }
        }

    }
}
